#!/bin/bash

# --------------------
# 初始变量
# --------------------
snmp_strings_file="$HOME/.ansible/snmp_strings.txt"  # 存放snmp字符串的文件
hostname=""
host=""
port=""
tag=""

# --------------------
# 定义函数
# --------------------

# 打印帮助信息
function help_msg {
cat << .EOF
参数说明：
  -h, --help:        打印帮助信息
  -n, --hostname:    设备名，报告的时候用指定的名称替代IP地址
  -p, --port:        登录设备协议使用的端口号
  -t, --tag:         设备类型: cisco, huawei, h3cv1, h3cv2
设备支持列表：
  --tag=h3cv1:  H3C 的 S3600v1 通过 telnet 登录的设备
  --tag=h3cv2:  H3C 的其他类型设备，通过 SSH 登录    
  --tag=cisco:  Cisco 通过 SSH 登录的设备
  --tag=huawei: HUAWEI 通过 SSH 登录的设备
SSH 协议扫描的端口列表和顺序：[2395, 22]
.EOF
}

# 通过SNMP获取设备类型并返回，返回值赋值给 tag 变量
function snmp_get_tag {
    sysDescr=""
    #snmp_strings=('public1' 'public2' 'public3' 'public')
    # 检查文件是否存在
    [ ! -f "$snmp_strings_file" ] && echo "文件\"${snmp_strings_file}\"不存在" >&2 && exit 1
    # 从文件中按行读取。文件加密，使用ansible-vault读取
    #ansible-vault view "$snmp_strings_file" | while read line
    # 注意：不能使用管道传递给while，这样while就在一个子shell中。
    # while中的变量在循环结束后就被丢弃了，这里的效果就是数组中的元素只在循环内有效，循环外这些元素都没有了
    while read line
    do
        snmp_strings=("${snmp_strings[@]}" "$line")
    done <<< "$(ansible-vault view $snmp_strings_file)"  # Here Strings 写法
    #done < <(ansible-vault view $snmp_strings_file)  # 进程替换写法，同样有效
    for snmp_string in "${snmp_strings[@]}"
    do
        sysDescr=$(snmpget -r2 -t1 -v2c -c $snmp_string $host sysDescr.0 2>/dev/null)
        #sysDescr=$(snmpget -r2 -t1 -v2c -c $snmp_string $host sysDescr.0)
        if [[ "$sysDescr" =~ ^'SNMPv2-MIB::sysDescr.0 = STRING:' ]]
        then
            break
        fi
    done
    # 无法通过SNMP获取到值，报错并退出
    if [ -z "$sysDescr" ]
    then
        echo "ERROR: 无法通过 SNMP 获取设备类型，请使用 --tag 参数指定。" >&2
        return 2
    fi
    # 根据获取到的值，判断设备类型
    if [[ ${sysDescr,,} =~ h3c ]]
    then
        #machine_type=$(echo "${sysDescr}" | sed -n 2p)  # 获取第二行的内容，不过发现不是固定出现在第二行
        if [[ ${sysDescr,,} =~ "s3600-" ]]
        then
            echo "h3cv1"
        else
            echo "h3cv2"
        fi
    elif [[ ${sysDescr,,} =~ cisco ]]
    then
        echo "cisco"
    elif [[ ${sysDescr,,} =~ huawei ]]
    then
        echo "huawei"
    else
        echo "ERROR: 通过 SNMP 无法确认设备类型，请使用 --tag 参数指定。" >&2
        echo "$sysDescr"
        return 2
    fi
}

# 端口扫描，返回第一个开放的端口号
function scan_port {
    opened_port=""
    port_list=(2395 22)  # telnet端口不扫描。h3cv1都是telnet，其他不支持telnet
    for port in "${port_list[@]}"
    do
        echo ""|telnet $host $port 2>/dev/null|grep "Escape character is"&>/dev/null
        [ $? == 0 ] && opened_port=$port && break
        #echo "扫描端口 $port 未开放"
    done
    if [ -z "$opened_port" ]
    then
        echo "ERROR: 未发现开放的端口，请使用 --port 参数指定。" >&2
        return 2
    fi
    echo "$opened_port"
}

# --------------------
# 解析命令行参数
# --------------------
getopt_cmd=$(getopt -o hn:p:t: --long help,hostname:,port:,tag: -n $(basename $0) -- "$@")
[ $? -ne 0 ] && exit 1
eval set -- "$getopt_cmd"
# 解析选项参数
while [ -n "$1" ]
do
    case "$1" in
        -h|--help)
            help_msg
            exit ;;
        -n|--hostname)
            hostname="$2"
            shift ;;
        -p|--port)
            port="$2"
            shift ;;
        -t|--tag)
            tag="$2"
            shift ;;
        --)
            shift
            break ;;
        *)
            echo "$1 is not an option"
            exit 1 ;;
    esac
    shift
done
# 解析位置参数
if [ $# != 2 ]
then
    echo "ERROR：参数错误：位置参数必须要2个，第一个指定设备，第二个指定端口名称"
    exit 1
fi
host=${1%%:*}
interfacename=$2
# =~正则匹配，是否包含冒号
[ -z "$port" ] && [[ $1 =~ : ]] && port=${1##*:}

# --------------------
# 自动获取选项参数
# --------------------

# 自动获取 tag 值
if [ -z "$tag" ]
then
    tag=$(snmp_get_tag)
    # 返回值非0，则退出。
    # 如果已经获取到了设备信息，只是没有匹配的设备类型，则输出获取的设备信息。
    if [ $? -ne 0 ]
    then
        [ -n "$tag" ] && echo -e "已获得的设备信息：\n$tag"
        exit 1
    fi
fi
echo "设备类型：$tag"

# h3cv1 都是telnet
[ "$tag" = "h3cv1" ] && port=23

# 扫描端口
if [ -z "$port" ]
then
    port=$(scan_port)
    if [ $? -ne 0 ]
    then
        echo -e "说明："
        echo -e "\tH3C 的 S3600v1 设备是通过 telnet 登录的。"
        echo -e "\tCisco、HUAWEI 以及 H3C 的其他设备都只支持 SSH 登录。"
        exit 1
    fi
fi
echo "开放端口：$port"

# 拼接生成 inventory 文件
if [ -n "$hostname" ]
then
    inventory_line="${hostname} ansible_host=${host}"
else
    inventory_line="${host}"
    hostname="${host}"
fi
inventory_line=" ${inventory_line} ansible_ssh_port=${port}"
echo "${inventory_line}" > ~/line_check_hosts
cmd_line="ansible-playbook -i ~/line_check_hosts --limit $hostname -e interfacename=$interfacename --tag=$tag --skip-tags=getconfig /opt/SdsAnsible/line_check.yml"
echo "执行命令：$cmd_line"

# -------------------------
# 调整ANSIBLE环境变量并启动
# -------------------------

# 让用户看不到 [DEPRECATION WARNING] 的信息
ANSIBLE_DEPRECATION_WARNINGS=False
# 上面创建的只是局部环境变量，需要用 export 命令导出到全局环境变量中
export ANSIBLE_DEPRECATION_WARNINGS
# 执行命令
eval "$cmd_line"
# 删除环境变量
unset ANSIBLE_DEPRECATION_WARNINGS
